package com.yuefresh.util;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * Extends Apache Commons BeanUtils
 */
@SuppressWarnings("unchecked")
public class BeanUtil extends BeanUtils {

	protected static final Log logger = LogFactory.getLog(BeanUtil.class);

	private BeanUtil() {}
	
	/////////////////////////////////////////////////////////////////////////////////////////////////
	private static Map classCache = new HashMap();
	public static Class loadClass(String className){
    	return loadClass(className, null);
    }
	
	/** 
     * Loads a class with the URLUtil's classpath.
     * @param className The name of the class to load
     * @param loader The ClassLoader to su
     * @return The requested class
     * @throws ClassNotFoundException
     */
    public static Class loadClass(String className, ClassLoader loader){

        Class theClass;

        //if (loader == null) loader = URLUtil.classpath.getClassLoader();
        if (loader == null) loader = Thread.currentThread().getContextClassLoader();

        try {
            theClass = loader.loadClass(className);
        } catch (Exception e) {
            theClass = (Class) classCache.get(className);
            if (theClass == null) {
                synchronized (BeanUtil.class) {
                    theClass = (Class) classCache.get(className);
                    if (theClass == null) {
                        try {
                            theClass = Class.forName(className);
                        } catch (ClassNotFoundException e1) {
                            throw new InfrastructureException("Class is not found. [" + className + "]");
                        }
                        if (theClass != null) {
                            if (logger.isInfoEnabled()) {
                            	logger.info("Loaded Class: " + theClass.getName());
                            }
                            classCache.put(className, theClass);
                        }
                    }
                }
            }
        }

        return theClass;
    }
    
    /**
     * Get an instance of the given class name.
     * @param className
     * @return
     * @throws NestedRuntimeException
     */
    public static Object getObject(String className) throws NestedRuntimeException{
        Class clazz;
        try {
            clazz = loadClass(className);
            return clazz.newInstance();
        } catch (InstantiationException e) {
            if (logger.isErrorEnabled())
            	logger.error(className + " : Class is cant instantialized.");
            throw new InfrastructureException(className + " : Class is cant instantialized.");
        } catch (IllegalAccessException e) {
            if (logger.isErrorEnabled())
            	logger.error(className + " : Class is not accessed.");
            throw new InfrastructureException(className + " : Class is not accessed.");
        }
    }

    /**
	 * 通过反射,获得定义Class时声明的父类的泛型参数的类型. 如public UserDao extends HibernateDao<User>
	 *
	 * @param clazz The class to introspect
	 * @return the first generic declaration, or Object.class if cannot be determined
	 */
	@SuppressWarnings("unchecked")
	public static Class getSuperClassGenricType(final Class clazz) {
		return getSuperClassGenricType(clazz, 0);
	}

	/**
	 * 通过反射,获得定义Class时声明的父类的泛型参数的类型. 如public UserDao extends HibernateDao<User,Long>
	 *
	 * @param clazz clazz The class to introspect
	 * @param index the Index of the generic ddeclaration,start from 0.
	 * @return the index generic declaration, or Object.class if cannot be determined
	 */

	@SuppressWarnings("unchecked")
	public static Class getSuperClassGenricType(final Class clazz, final int index) {

		Type genType = clazz.getGenericSuperclass();
		
		//让泛型基类可以支持二层继承
		if (!(genType instanceof ParameterizedType)) {
			Class superClazz = clazz.getSuperclass();
			genType = superClazz.getGenericSuperclass();
		}

		if (!(genType instanceof ParameterizedType)) {
			logger.warn(clazz.getSimpleName() + "'s superclass not ParameterizedType");
			return Object.class;
		}

		Type[] params = ((ParameterizedType) genType).getActualTypeArguments();

		if (index >= params.length || index < 0) {
			logger.warn("Index: " + index + ", Size of " + clazz.getSimpleName() + "'s Parameterized Type: "
					+ params.length);
			return Object.class;
		}
		if (!(params[index] instanceof Class)) {
			logger.warn(clazz.getSimpleName() + " not set the actual class on superclass generic parameter");
			return Object.class;
		}
		return (Class) params[index];
	}
    
    /**
     * get object from original object by property name
     * @param object
     * @param name
     * @return
     */
    public static Object getValue(Object object, String name) {
        try {
            return PropertyUtils.getProperty(object, name);
        } catch (IllegalAccessException e) {
            if (logger.isErrorEnabled())
            	logger.error(name + " : Class is not accessed.");
            throw new InfrastructureException(name + " : Class is not accessed.");
        } catch (InvocationTargetException e) {
            if (logger.isErrorEnabled())
            	logger.error(name + " : Invocation error.");
            throw new InfrastructureException(name + " : Invocation error.");
        } catch (NoSuchMethodException e) {
            if (logger.isErrorEnabled())
            	logger.error(name + " : Class has no such method.");
            throw new InfrastructureException(" Class has no such method for property: "+name+".");
        }
    }
    
    /**
     * @param bean
     * @param propertyName
     * @return
     */
    public static boolean hasProperty(Object bean, String propertyName) {
        try {
            Field field = bean.getClass().getDeclaredField(propertyName);
            if (field != null) {
                return true;
            }
        } catch (SecurityException e) {
            if (logger.isErrorEnabled())
            	logger.error(propertyName + " : Class is not Security.");
            throw new InfrastructureException(propertyName + " : Class is not Security.");
        } catch (NoSuchFieldException e) {
            return false;
        }
        return false;
    }
    
    /**
     * 
     * @param object
     * @param name    property name
     * @param value   property value
     */
	public static void setValue(Object object, String name, Object value) {
		try {
			PropertyUtils.setProperty(object, name, value);
		} catch (IllegalAccessException e) {
            if (logger.isErrorEnabled())
            	logger.error(name + " : Class is not accessed.");
            throw new InfrastructureException(name + " : Class is not accessed.");
        } catch (InvocationTargetException e) {
            if (logger.isErrorEnabled())
            	logger.error(name + " : Invocation error.");
            throw new InfrastructureException(name + " : Invocation error.");
        } catch (NoSuchMethodException e) {
            if (logger.isErrorEnabled())
            	logger.error(name + " : Class has no such method.");
            throw new InfrastructureException(name + " : Class has no such method.");
        }
	}
}
